package com.ms.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ms.constant.GenreTypeConstant;
import com.ms.constant.MessageConstant;
import com.ms.domain.dto.GenreUpdateDTO;
import com.ms.domain.entity.Genre;
import com.ms.domain.entity.Music;
import com.ms.domain.vo.GenreSearchVO;
import com.ms.exception.BusinessException;
import com.ms.exception.ResourceNotFoundException;
import com.ms.mapper.GenreMapper;
import com.ms.service.GenreService;
import com.ms.service.MusicService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor
public class GenreServiceImpl implements GenreService {

    private final GenreMapper genreMapper;
    private final MusicService musicService;

    /**
     * 根据父级分类获取子分类
     *
     * @param parentId 父级分类ID
     * @param type     分类类型
     * @return 分类列表
     */
    @Override
    @Cacheable(value = "genres",
            key = "T(com.ms.constant.CacheConstant).GENRES_BY_PARENT + ':' + #parentId + ':' + #type",
            unless = "#result == null || #result.isEmpty()")
    public List<Genre> getGenresByParent(Integer parentId, String type) {
        log.info("获取子分类: 父级分类ID={}, 子分类类型={}", parentId, type);
        return genreMapper.selectList(Wrappers.<Genre>lambdaQuery()
                .eq(Genre::getParentId, parentId)
                .eq(Genre::getType, type));
    }

    /**
     * 获取所有分类按照顺序获取
     * @return 分类列表
     */
    @Cacheable(value = "genres", key = "T(com.ms.constant.CacheConstant).ALL_GENRES",
            unless = "#result == null || #result.isEmpty()")
    public List<Genre> getAllGenres() {
        return genreMapper.selectAllSorted();
    }

    /**
     * 根据分类类型获取分类列表
     *
     * @param type 分类类型
     * @return 分类列表
     */
    @Override
    @Cacheable(value = "genres",
            key = "T(com.ms.constant.CacheConstant).GENRES_BY_TYPE + ':' + #type",
            unless = "#result == null || #result.isEmpty()")
    public List<Genre> getGenresByType(String type) {
        return genreMapper.selectList(Wrappers.<Genre>lambdaQuery().eq(Genre::getType, type));
    }

    /**
     * 创建分类
     * @param genre  分类
     */
    @Override
    @Transactional
    @Caching(evict = {
            @CacheEvict(value = "genres", key = "T(com.ms.constant.CacheConstant).ALL_GENRES"),
            @CacheEvict(value = "genres", key = "T(com.ms.constant.CacheConstant).GENRES_BY_TYPE + '*'"),
            @CacheEvict(value = "genres", key = "T(com.ms.constant.CacheConstant).GENRES_BY_PARENT + '*'"),
            @CacheEvict(value = "genres", key = "T(com.ms.constant.CacheConstant).SEARCH_GENRES + '*'"),
            @CacheEvict(value = "genres", key = "T(com.ms.constant.CacheConstant).SEARCH_GENRES_PAGE + '*'"),
            @CacheEvict(value = "genreTree", allEntries = true)
    })
    public void createGenre(Genre genre) {
        // 验证逻辑
        if (GenreTypeConstant.MAIN.equals(genre.getType()) && genre.getParentId() != null) {
            throw new BusinessException("一级分类不能有父分类");
        }
        if (!GenreTypeConstant.MAIN.equals(genre.getType()) && genre.getParentId() == null) {
            throw new BusinessException("子分类必须指定父分类");
        }

        genreMapper.insert(genre);

        // 更新levelPath
        String levelPath = genre.getId().toString();
        if (genre.getLevelPath() == null) {
            // 如果是二级分类
            if (GenreTypeConstant.SUB.equals(genre.getType())) {
                levelPath = genre.getParentId().toString() + "-" + genre.getId();
            }
            // 如果是三级分类
            if (GenreTypeConstant.SUBTYPE.equals(genre.getType())) {
                String parentLevelPath = genreMapper.selectById(genre.getParentId()).getLevelPath();
                levelPath = parentLevelPath + "-" + genre.getId();
            }
        }
        genre.setLevelPath(levelPath);
        genreMapper.updateById(genre);
    }

    /**
     * 获取分类树形结构
     * @return 分类树形结构
     */
    @Override
    @Cacheable(value = "genreTree", key = "'all'",
            unless = "#result == null || #result.isEmpty()")
    public List<Map<String, Object>> getGenreTree() {
        // 1. 获取所有分类数据（已排序）
        List<Genre> allGenres = getAllGenres();

        // 2. 按分类类型分组（MAIN/SUB/SUBTYPE）
        Map<String, List<Genre>> byType = allGenres.stream()
                .collect(Collectors.groupingBy(Genre::getType));

        // 3. 构建树形结构
        return byType.getOrDefault(GenreTypeConstant.MAIN, Collections.emptyList()).stream()
                .map(main -> {
                    // 3.1 创建一级分类节点
                    Map<String, Object> mainNode = new HashMap<>();
                    mainNode.put("id", main.getId());
                    mainNode.put("name", main.getName());
                    mainNode.put("type", main.getType());

                    // 3.2 为一级分类查找二级子分类
                    List<Map<String, Object>> subNodes = byType.getOrDefault(GenreTypeConstant.SUB, Collections.emptyList()).stream()
                            .filter(sub -> main.getId().equals(sub.getParentId()))
                            .map(sub -> {
                                // 3.2.1 创建二级分类节点
                                Map<String, Object> subNode = new HashMap<>();
                                subNode.put("id", sub.getId());
                                subNode.put("name", sub.getName());
                                subNode.put("type", sub.getType());

                                // 3.2.2 为二级分类查找三级子分类
                                List<Map<String, Object>> typeNodes = byType.getOrDefault(GenreTypeConstant.SUBTYPE, Collections.emptyList()).stream()
                                        .filter(type -> sub.getId().equals(type.getParentId()))
                                        .map(type -> {
                                            // 3.2.3 创建三级分类节点
                                            Map<String, Object> typeNode = new HashMap<>();
                                            typeNode.put("id", type.getId());
                                            typeNode.put("name", type.getName());
                                            typeNode.put("type", type.getType());
                                            return typeNode;
                                        })
                                        .collect(Collectors.toList());

                                // 3.2.3 将三级分类挂到二级节点下
                                subNode.put("children", typeNodes);
                                return subNode;
                            })
                            .collect(Collectors.toList());

                    // 3.3 将二级分类挂到一级节点下
                    mainNode.put("children", subNodes);
                    return mainNode;
                })
                .collect(Collectors.toList());
    }

    /**
     * 修改分类
     *
     * @param id  分类id
     * @param dto 修改参数
     */
    @Override
    @Transactional
    @Caching(evict = {
            @CacheEvict(value = "genres", key = "T(com.ms.constant.CacheConstant).ALL_GENRES"),
            @CacheEvict(value = "genres", key = "T(com.ms.constant.CacheConstant).GENRES_BY_TYPE + '*'"),
            @CacheEvict(value = "genres", key = "T(com.ms.constant.CacheConstant).GENRES_BY_PARENT + '*'"),
            @CacheEvict(value = "genres", key = "T(com.ms.constant.CacheConstant).SEARCH_GENRES + '*'"),
            @CacheEvict(value = "genres", key = "T(com.ms.constant.CacheConstant).SEARCH_GENRES_PAGE + '*'"),
            @CacheEvict(value = "genres", key = "T(com.ms.constant.CacheConstant).GENRES_BY_MUSIC + '*'"),
            @CacheEvict(value = "genreTree", allEntries = true)
    })
    public void updateGenre(Integer id, GenreUpdateDTO dto) {
        // 修改分类：可以修改名称，描述
        // 1. 查询分类是否存在
        if (StringUtils.isBlank(dto.getName())) {
            throw new BusinessException(MessageConstant.GENRE_NAME_EMPTY);
        }
        Genre genre = genreMapper.selectById(id);
        if (genre == null) {
            throw new ResourceNotFoundException(MessageConstant.GENRE_NOT_FOUND);
        }
        // 添加类型验证
        if (!Arrays.asList("MAIN", "SUB", "SUBTYPE").contains(genre.getType())) {
            throw new BusinessException(MessageConstant.INVALID_GENRE_TYPE);
        }

        // 检查名称唯一性（避免冲突）
        if (!genre.getName().equals(dto.getName())) {
            long count = genreMapper.selectCount(
                    Wrappers.<Genre>lambdaQuery()
                            .eq(Genre::getName, dto.getName())
//                    new QueryWrapper<Genre>()
//                    .eq("name", dto.getName())
//                    .ne("id", id)
            );
            if (count > 0) {
                throw new BusinessException(MessageConstant.GENRE_NAME_EXISTS);
            }
        }

        // 更新分类信息
        genre.setName(dto.getName());
        genre.setDescription(dto.getDescription());
        genreMapper.updateById(genre);

        // 处理父ID修改（仅当dto中包含parentId时）
        if (dto.getParentId() != null) {
            // 如果parent_id被修改
            if (!genre.getParentId().equals(dto.getParentId())) {
                if (GenreTypeConstant.MAIN.equals(genre.getType())) {
                    // 如果是主分类，更新所有子分类的parent_id和level_path
                    genreMapper.update(null, new UpdateWrapper<Genre>()
                            .eq("parent_id", id)
                            .set("parent_id", genre.getParentId())
                            .set("level_path", genre.getParentId() + "-" + id));

                    // 更新所有子子分类的level_path
                    List<Genre> subGenres = genreMapper.selectList(new QueryWrapper<Genre>().eq("parent_id", id));
                    for (Genre subGenre : subGenres) {
                        subGenre.setLevelPath(genre.getParentId() + "-" + id + "-" + subGenre.getId());
                        genreMapper.updateById(subGenre);
                    }
                } else if (GenreTypeConstant.SUB.equals(genre.getType())) {
                    // 如果是子分类，更新level_path
                    genre.setLevelPath(genre.getParentId() + "-" + id);
                    genreMapper.updateById(genre);

                    // 更新所有子子分类的level_path
                    List<Genre> subTypeGenres = genreMapper.selectList(new QueryWrapper<Genre>().eq("parent_id", id));
                    for (Genre subTypeGenre : subTypeGenres) {
                        subTypeGenre.setLevelPath(id + "-" + subTypeGenre.getId());
                        genreMapper.updateById(subTypeGenre);
                    }
                }
            }
        }
    }

    /**
     * 删除分类
     *
     * @param id 分类id
     */
    @Override
    @Transactional
    @Caching(evict = {
            @CacheEvict(value = "genres", key = "T(com.ms.constant.CacheConstant).ALL_GENRES"),
            @CacheEvict(value = "genres", key = "T(com.ms.constant.CacheConstant).GENRES_BY_TYPE + '*'"),
            @CacheEvict(value = "genres", key = "T(com.ms.constant.CacheConstant).GENRES_BY_PARENT + '*'"),
            @CacheEvict(value = "genres", key = "T(com.ms.constant.CacheConstant).SEARCH_GENRES + '*'"),
            @CacheEvict(value = "genres", key = "T(com.ms.constant.CacheConstant).SEARCH_GENRES_PAGE + '*'"),
            @CacheEvict(value = "genres", key = "T(com.ms.constant.CacheConstant).GENRES_BY_MUSIC + '*'"),
            @CacheEvict(value = "genreTree", allEntries = true)
    })
    public void deleteGenre(Integer id) {
        Genre genre = genreMapper.selectById(id);
        if (genre == null) {
            throw new ResourceNotFoundException(MessageConstant.GENRE_NOT_FOUND);
        }

        // 1. 检查子分类
        if (GenreTypeConstant.MAIN.equals(genre.getType()) || GenreTypeConstant.SUB.equals(genre.getType())) {
            long childCount = genreMapper.selectList(new QueryWrapper<Genre>()
                    .eq("parent_id", id)).size();

            if (childCount > 0) {
                throw new BusinessException(MessageConstant.GENRE_HAS_CHILDREN);
            }
        }

        // 2. 检查音乐关联
        if (!musicService.getMusicByGenre(id).isEmpty()) {
            throw new BusinessException("有音乐使用此分类，无法删除");
        }

        // 3. 执行删除
        genreMapper.deleteById(id);
    }

    /**
     * 搜索分类
     *
     * @param keyword 关键字
     * @param type    类型
     * @return 分类列表
     */
    @Override
    @Cacheable(value = "genres",
            key = "T(com.ms.constant.CacheConstant).SEARCH_GENRES + ':' + #keyword + ':' + #type",
            unless = "#result == null || #result.isEmpty()")
    public List<GenreSearchVO> searchGenres(String keyword, String type) {
        QueryWrapper<Genre> wrapper = new QueryWrapper<>();

        // 关键字搜索（名称或父级）
        if (StringUtils.isNotBlank(keyword)) {
            wrapper.and(qw -> qw
                    .like("name", keyword)
                    .or()
                    .like("parent", keyword));
        }

        // 按类型过滤
        if (StringUtils.isNotBlank(type) &&
                Arrays.asList(GenreTypeConstant.MAIN, GenreTypeConstant.SUB, GenreTypeConstant.SUBTYPE).contains(type)) {
            wrapper.eq("type", type);
        }

        // 执行查询
        List<Genre> genres = genreMapper.selectList(wrapper);

        // 转换为VO
        return genres.stream().map(g -> {
            GenreSearchVO vo = new GenreSearchVO();
            vo.setId(g.getId());
            vo.setName(g.getName());
            vo.setType(g.getType());
            vo.setParentId(g.getParentId());
            return vo;
        }).collect(Collectors.toList());
    }

    /**
     * 分页搜索分类
     *
     * @param keyword 关键字
     * @param type    类型
     * @param page    页码
     * @param size    每页数量
     * @return 分类分页数据
     */
    @Override
    @Cacheable(value = "genres",
            key = "T(com.ms.constant.CacheConstant).SEARCH_GENRES_PAGE + ':' + #keyword + ':' + #type + ':' + #page + ':' + #size",
            unless = "#result == null || #result.records.isEmpty()")
    public Page<GenreSearchVO> searchGenresPage(String keyword, String type, Integer page, Integer size) {
        QueryWrapper<Genre> wrapper = new QueryWrapper<>();

        // 关键字搜索（名称或父级）
        if (StringUtils.isNotBlank(keyword)) {
            wrapper.and(qw -> qw
                    .like("name", keyword)
                    .or()
                    .like("parent", keyword));
        }

        // 按类型过滤
        if (StringUtils.isNotBlank(type) &&
                Arrays.asList(GenreTypeConstant.MAIN, GenreTypeConstant.SUB, GenreTypeConstant.SUBTYPE).contains(type)) {
            wrapper.eq("type", type);
        }

        // 创建分页对象
        Page<Genre> genrePage = new Page<>(page, size);

        // 执行分页查询
        genreMapper.selectPage(genrePage, wrapper);

        // 转换为VO
        List<GenreSearchVO> voList = genrePage.getRecords().stream().map(g -> {
            GenreSearchVO vo = new GenreSearchVO();
            vo.setId(g.getId());
            vo.setName(g.getName());
            vo.setType(g.getType());
            vo.setParentId(g.getParentId());
            return vo;
        }).collect(Collectors.toList());

        // 构建返回的分页对象
        Page<GenreSearchVO> resultPage = new Page<>(page, size, genrePage.getTotal());
        resultPage.setRecords(voList);

        return resultPage;
    }

    /**
     * 根据音乐id获取三级曲风
      * @param musicId 音乐ID
      * @return 三级曲风列表
     */
    @Override
    @Cacheable(value = "genres",
            key = "T(com.ms.constant.CacheConstant).GENRES_BY_MUSIC + ':' + #musicId",
            unless = "#result == null || #result.isEmpty()")
    public List<Genre> getGenreByMusicId(Integer musicId) {
        // 判断音乐是否存在
        Music music = musicService.getMusicById(musicId);
        if (music == null) {
            throw new ResourceNotFoundException(MessageConstant.MUSIC_NOT_FOUND);
        }
        List<String> genrePath = List.of(genreMapper.selectOne(
                Wrappers.<Genre>lambdaQuery().eq(Genre::getId, music.getGenreId())
        ).getLevelPath().split("-"));
        log.info("根据音乐id:{}获取三级曲风id:{}", musicId, genrePath);
        return genreMapper.selectList(
                Wrappers.<Genre>lambdaQuery().in(Genre::getId, genrePath)
        );
    }

    /**
     * 获取分类
     * @param id 分类id
     * @return  分类
     */
    @Override
    @Cacheable(value = "genres",
            key = "T(com.ms.constant.CacheConstant).GENRE_BY_ID + ':' + #id",
            unless = "#result == null")
    public Genre getGenre(Integer id) {
        return genreMapper.selectById(id);
    }
}